//GRANI (graphics and animation)
//part of the AXL (allegro XML based library) project
//by neil walker 2005

#ifndef axl_grani
#define axl_grani

#include "axl.h"

//collision detection routines for PPCOL. see PPCOL
#define check_bb_collision_general(x1,y1,w1,h1,x2,y2,w2,h2) (!( ((x1)>=(x2)+(w2)) || ((x2)>=(x1)+(w1)) || ((y1)>=(y2)+(h2)) || ((y2)>=(y1)+(h1)) ))
#define check_bb_collision(mask1,mask2,x1,y1,x2,y2) check_bb_collision_general(x1,y1,mask1->w,mask1->h,x2,y2,mask2->w,mask2->h)

namespace AXL_Projects {
	//loop types for animations
	enum LoopType{LOOP_INVALID=-1, LOOP_START=0,LOOP_REVERSEREPEAT,LOOP_REVERSEONCE, LOOP_REVERSEBACK, LOOP_ONCE};
	//how the bitmap was created. Default is memory
#if (USE_OPENLAYER!=1)
	enum BitmapType{TYPE_VIDEO=3, TYPE_SYSTEM=2, TYPE_MEMORY=1};
#else
	enum BitmapType{TYPE_VIDEO=3};
#endif
	// library contains all the items details
	// animation contains all the actual items for each sprite

	//forward declarations
	class AnimationLibrary;
	struct AnimationLibraryFrame;
	class AnimationLibraryFrames;


	/*
		Mask routines take from:
		This is version 1.3 of the PPCol library.
		Made by Ivan Baldo.
		This library contains slightly modified code borrowed from:
		Original bitmasked routines by Neil Chinnery - neich@geocities.com
		Bitmasked routines enhanced by Michael de la Pena (Vox) - vox@bellsouth.net
	*/
	typedef struct PPCOL_MASK
	{
		int h;                          //Bounding box height.
		int w;                          //Bounding box width.
		int max_chunk;                  //This determines the total number of 32bits chunks - 1.
		void *dat;                      //The mask data pointer.
		unsigned long int *sp_mask[0];  //Pointers to the begining of each row of chunks.
	} PPCOL_MASK;

	//actual details of a single animation frame, managed entirely by the animationlibraryframes
	//namespace axl_system {
	class Animation
	{
	public:
		//constructor for either a search string to find a name or if already prefound for speed
		Animation(AnimationLibrary* const al, const std::string& item);
		Animation(AnimationLibrary* const al, AnimationLibraryFrames* const item);
		~Animation();
		void NextMove();		//if auto increments frame counts and determines next graphic, if manual then gets next graphic
#if (OPTIMISE_NOFADE==0) || (OPTIMISE_NOCOUNTERS==0)		
		void ManualTimerUpdate();	//if in manual step mode then fades/timers wouldn't get updated properly so 
									//run this every frame update if manual mode to increment properly
#else
		inline void ManualTimerUpdate() {};
#endif
		void SetNewSpeedMS(int ms);	//set the speed for all animations as an override
		void SetNewSpeedFrameCount(int fc);
		void SetNewLoopType(const LoopType lt);
		bool SetNewAnimation(AnimationLibraryFrames* const af);
		bool SetNewAnimation(const std::string& id);
		void SetAutoMode(bool auto);
		void GetStandardDimensions(int* w, int* h) const;
		bool GetAutoMode() const;
		int GetCurrentSpeedMS() const;
		int GetCurrentSpeedFrameCount() const;	//animation item
		void Pause();	//pause it
		void Resume();	//resume from a pause
		void Reset();	//reset to defaults
#if(OPTIMISE_NOFADE	== 0)
		void FadeOut(int milliseconds, bool alterbmp=true,int colour=0, int startinglevel=-1);	//fade sprite to nothing in xx milliseconds		
		void FadeIn(int milliseconds, bool alterbmp=true,int colour=0, int startinglevel=-1);	//fade to full image in xx milliseconds
#else
		inline void FadeOut(int milliseconds, bool alterbmp=true,int colour=0, int startinglevel=-1) {};	//do nothing
		inline void FadeIn(int milliseconds, bool alterbmp=true,int colour=0, int startinglevel=-1) {};	//do nothing
#endif

		//data - data items for speed but are really read only
#if (OPTIMISE_NOCOUNTERS==0)
		void SetCustomCountDown(int timer, int milliseconds);	//1 to (MAX_COUNTDOWN_TIMERS-1) only (0 is the fade timer)
#else
		inline void SetCustomCountDown(int timer, int milliseconds) {};	//do nothing
#endif
		AXL_BITMAP* ReadOnly_CurrentItem;	//graphic for the current frame
		bool ReadOnly_EndReached;		//for sequences that end (do not loop indefinitely) set to true when last animation has finished
		bool ReadOnly_Timers[MAX_COUNTDOWN_TIMERS];		//for count down timers, first is fading ended, remainder are for user custom timers
		PPCOL_MASK* ReadOnly_CurrentPPCOLMask;		//get the pixel perfect collision mask of the current frame. NULL if none available
#if (USE_OPENLAYER==1)
		ol::Poly* ReadOnly_CurrentCollisionPoly;
#endif
		BitmapType ReadOnly_TypeOfBitmap;	//BitmapType enumeration showing what bitmap type it is
		float ReadOnly_TimersCounter[MAX_COUNTDOWN_TIMERS];		//for count down timers, first is fading ended, remainder are for user customer timers. value of each item is the current value
#if (USE_OPENLAYER==1)
		int CheckCollision(int this_x, int this_y, ol::Poly* mask1, int mask1_x, int mask1_y) const;
#else
		int CheckCollision(int this_x, int this_y, PPCOL_MASK *mask1, int mask1_x, int mask1_y) const;
#endif		
		//below are private, public just for testing
	private:
		AnimationLibraryFrames* AnimationSequence;
		void Initialise(AnimationLibrary* const al);
		AnimationLibrary* Library;		//library we are using
		std::vector<AnimationLibraryFrame*>::iterator AnimationIterator;
		int CurrentFrameItem;	//current index of the animation
		int FrameCountOverride;	//if calling setnewspeed then this is stored here as an override
		bool GoingForward;	//tell which way the iterator is going
		bool Paused;
		bool ReverseOnceToggle;	//to track if it has been there and back
		bool FadingSpriteIn;		//are we fading it in
		bool FadingSpriteOut;
		bool FadingModifyBitmap;	//may just want to return the timer value for the fade
		float FadingStepInc;		//how many steps to increment each frame
		int FadingColour;
		AXL_BITMAP* FadingSpriteBMP;
		int ColourDepth;		//only used for speeding up fades fades
		int w,h;				//should use getstandarddimension but this is handy for fades
		int MaskColour;			//mask colour (e.g. magic pink, black). nothing to do with PPCOL!
#if (OPTIMISE_NOFADE==0) || (OPTIMISE_NOCOUNTERS==0)
		void TimerUpdate();		//timer helper. split from nextmove to allow manual timer update to work
#else
		inline void TimerUpdate() {};	//do nothing if both are defined
#endif

		void ManualTimerUpdateHelperBitmap();		//second stage of fading
		void ManualTimerUpdateHelperFade();			//second stage of fading	
	};

	struct AnimationLibraryFrame
	{
	public:
		friend class Animation;
		friend class AnimationLibrary;
		friend class AnimationLibraryFrames;
		AnimationLibraryFrame(AnimationLibraryFrames* const parent=NULL, const std::string& file="", int bitmaptype=-1, int msWait=-1, int fWait=-1, int isasheetgraphic=-1, int originx=-1, int originy=-1, int tilex=-1, int tiley=-1, int sheetitemwidth=-1, int sheetitemheight=-1,bool hasaPPCOLmask=false);
		~AnimationLibraryFrame(void);
	private:
		//see bottom of file
		PPCOL_MASK* PPCOLMask;
#if (USE_OPENLAYER==1)
		ol::Poly* FrameCollisionPoly;
#endif
		bool CreatePPCOLMask();
		void DeletePPCOLMask();

		std::string File;
		//int DataEntry;		dat files not implemented yet
		int FWait;
		int MSWait;
		int IsASheetGraphic;
		int OriginX;
		int OriginY;
		int TileX;
		int TileY;
		int SheetItemWidth;
		int SheetItemHeight;
		bool CreatedOk;			//constructor flag to inform creating code of success
		AXL_BITMAP* Image;
		bool HasPPCOLMask;
		BitmapType TypeOfBitmap;	
		//sheet location stuff is not stored as once the bitmap is created it is a full bitmap
	};

	//the actual animation sequence of animation frames
	class AnimationLibraryFrames
	{
	public:
		friend class Animation;
		friend class AnimationLibrary;
		friend struct AnimationLibraryFrame;
		AnimationLibraryFrames(const std::string& id, LoopType lt=LOOP_INVALID,int msWait=-1, int fWait=-1,
			bool autom=true, int depth=-1, int dox=-1, int doy=-1, int dsg=-1, int dsw=-1, 
			int dsh=-1, int disa=-1, int dbitmaptype=-1, const std::string& filename="",bool hasaPPCOLmask=false);
		~AnimationLibraryFrames(void);
		std::string Id;
		bool AddAnimationFrame(AnimationLibraryFrame* const ai);
	private:
		LoopType Loop;			//how it loops
		bool AutoMove;			//auto or manual stepping required
		bool OriginalAutoMove;
		std::vector<AnimationLibraryFrame*> FrameItems;
		LoopType OriginalLoop;		//in case of setting outside

		//static - using here to avoid passing parent/child links up/down the chain
		int DefaultMilliSecondWait;	//wait between animation items
		int DefaultFrameWait;			//as above but frame counts
		int DefaultColourDepth;
		int DefaultSheetWidth, DefaultSheetHeight, DefaultOriginX, DefaultOriginY;
		bool DefaultIsASheetGraphic;
		bool DefaultSheetGrid;
		bool DefaultHasPPCOLMask;
		std::string DefaultFileName;
		BitmapType DefaultTypeOfBitmap;
		BitmapType MaxType;		//used to limit the graphics type created. useful if animation requests video but the games buffer was only initialised as a memory (VRAM->MEMORY is slow)
		
		AXL_BITMAP* (*BitmapCreator)(const std::string& bmpname);
		void SetLoader(AXL_BITMAP* (*bitmapcreator)(const std::string& bmpname)) {
			BitmapCreator=bitmapcreator;
		}

	};

	class AnimationLibrary
	{
	public:
	//	friend class Animation;
	//	friend struct AnimationLibraryFrame;
		//file below can also be a string
		AnimationLibrary(const std::string& file="animations.xml", int fps=60, BitmapType maxbmptype=TYPE_VIDEO,AXL_BITMAP* (*bitmapcreator)(const std::string&)=NULL);
		~AnimationLibrary(void);
		//normally you use animation class to get animation
		//but this can be called to either make it faster
		//or when you aren't bothered about the animation class
		AnimationLibraryFrames* GetAnimation(const std::string& id) const;
		AXL_BITMAP* GetFirstGraphic(const std::string& id) const;
		void DestroyAll();
		void draw() const;
		bool LoadError;	//if framework not being used, sets if error during constructor/loading
		bool AddAnimation(AnimationLibraryFrames* const animation);
		bool LoadAnimations(const std::string& file="animations.xml");
		void DebugData(const std::string& file="animationslogdebug.log") const;
		int fps;
		static std::map<std::string,AXL_BITMAP*> StoredBitmaps;	//ensure when loading images only loaded once if referenced multiple times
		static void AnimationLibrary::LogEntry(const std::string& s, bool flag, const std::string& f);
		static void SetGlobalErrorString(const std::string& s);
		static const std::string GetGlobalErrorString();
		AXL_BITMAP* (*BitmapCreator)(const std::string& bmpname);

	private:
		std::map<std::string,AnimationLibraryFrames*> AnimationFrames;
		LoopType AnimationLibrary::GetLoopTypeFromChar(const char* loop) const;

		//statics - using here to avoid passing parent/child links up/down the chain
	public:
		static LoopType DefaultLoop;
		static int DefaultMilliSecondWait;
		static int DefaultFrameWait;
		static int DefaultColourDepth;
		static bool DefaultAutoMove;
		static int DefaultSheetWidth, DefaultSheetHeight;
		static bool DefaultSheetGrid;
		static bool DefaultIsASheetGraphic;
		static bool DefaultHasPPCOLMask;
		static BitmapType DefaultTypeOfBitmap;
		static BitmapType MaxTypeOfBitmap;
	};
}
#endif
